﻿using System;
using System.Linq.Expressions;
using Infrastructure.CrossCutting.Checks;

namespace Data.Core.Specifications
{
    public class Specification<T> : ISpecification<T>
    {

        #region .ctor
        
        public Specification(Expression<Func<T, bool>> predicate)
        {
            Check.Against<ArgumentNullException>(predicate == null, "Expected a non null expression as predicate for the specification.");
            Predicate = predicate;
            PredicateCompiled = predicate.Compile();
        }

        #endregion

        #region Implementation of ISpecification<T>

        /// <summary>
        /// Gets the expression that encapsulates the criteria of the specification.
        /// </summary>
        public Expression<Func<T, bool>> Predicate { get; private set; }

        /// <summary>
        /// Gets the expression compiled that encapsulates the criteria of the specification.
        /// </summary>
        public Func<T, bool> PredicateCompiled { get; private set; }

        /// <summary>
        /// Evaluates the specification against an entity of <typeparamref name="T"/>.
        /// </summary>
        /// <param name="entity">The <typeparamref name="T"/> instance to evaluate the specificaton
        /// against.</param>
        /// <returns>Should return true if the specification was satisfied by the entity, else false. </returns>
        public bool IsSatisfied(T entity)
        {
            return PredicateCompiled.Invoke(entity);
        }

        #endregion

        public static ISpecification<T> operator &(Specification<T> left, Specification<T> right)
        {
            return left.And(right);
        }

        public static ISpecification<T> operator |(Specification<T> left, Specification<T> right)
        {
            return left.Or(right);
        }
    }
}
